﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TheCopticProject.CopticCalendar;

namespace CopticCalendarUnitTest
{
    [TestClass]
    public class TestCopticDateCalculations
    {
        #region Test Constructors
        [TestMethod]
        public void TestDateValidations()
        {
            AssertExtensions.Throws<Exception>(() => { new Date(1730, 1, 41); }, "Invalid day 41");
            AssertExtensions.Throws<Exception>(() => { new Date(1730, 1, 0); }, "Invalid day 0");
            AssertExtensions.Throws<Exception>(() => { new Date(1730, 0, 1); }, "Invalid month 0");
            AssertExtensions.Throws<Exception>(() => { new Date(0, 1, 1); }, "Invalid year 0");
            AssertExtensions.Throws<Exception>(() => { new Date(1730, 14, 1); }, "Invalid month 14");
            AssertExtensions.Throws<Exception>(() => { new Date(1730, 13, 6); }, "Invalid month/day/year 6/13/1730");
            AssertExtensions.Throws<Exception>(() => { new Date(1730, 12, 31); }, "Invalid day 31");
            AssertExtensions.ThrowsNothing(() => { new Date(1, 1, 1); }, "Date 1/1/1");
            AssertExtensions.ThrowsNothing(() => { new Date(1730, 13, 1); }, "Date 1/13/1730");
            AssertExtensions.ThrowsNothing(() => { new Date(1730, 13, 5); }, "Date 5/13/1730");
            AssertExtensions.ThrowsNothing(() => { new Date(1727, 13, 6); }, "Date 6/13/1727");
            AssertExtensions.ThrowsNothing(() => { new Date(1730, 12, 1); }, "Date 1/12/1730");
            AssertExtensions.ThrowsNothing(() => { new Date(1730, 12, 30); }, "Date 30/12/1730");
            AssertExtensions.ThrowsNothing(() => { new Date(172430, 12, 30); }, "Date 30/12/172430");
        }

        [TestMethod]
        public void TestCreateFromGregorianDate()
        {
            Assert.AreEqual(new Date(1, 1, 1), new Date(new DateTime(284, 8, 29)), "29/8/284");
            Assert.AreEqual(new Date(1730, 4, 29), new Date(new DateTime(2014, 1, 7)), "7/1/2014");
            Assert.AreEqual(new Date(1730, 8, 12), new Date(new DateTime(2014, 4, 20)), "20/4/2014");
        }
        #endregion

        #region Test static utilities
        [TestMethod]
        public void TestGetMonthDays()
        {
            Assert.AreEqual(30, Date.GetMonthDays(1, 234235), "Month 1");
            Assert.AreEqual(30, Date.GetMonthDays(6, 234235), "Month 6");
            Assert.AreEqual(30, Date.GetMonthDays(12, 234235), "Month 12");
            Assert.AreEqual(5, Date.GetMonthDays(13, 1730), "Month 13/1730");
            Assert.AreEqual(6, Date.GetMonthDays(13, 1727), "Month 13/1727");
        }

        [TestMethod]
        public void TestGetYearDays()
        {
            Assert.AreEqual(365, Date.GetYearDays(1730), "Year 1730");
            Assert.AreEqual(366, Date.GetYearDays(1727), "year 1727");
        }

        [TestMethod]
        public void TestIsLeapYear()
        {
            Assert.IsFalse(Date.IsLeapYear(1730), "Year 1730");
            Assert.IsFalse(Date.IsLeapYear(1729), "Year 1729");
            Assert.IsFalse(Date.IsLeapYear(1728), "Year 1728");
            Assert.IsTrue(Date.IsLeapYear(1727), "year 1727");
            Assert.IsFalse(Date.IsLeapYear(1726), "Year 1726");
            Assert.IsFalse(Date.IsLeapYear(1725), "Year 1725");
            Assert.IsFalse(Date.IsLeapYear(1724), "Year 1724");
            Assert.IsTrue(Date.IsLeapYear(1723), "year 1723");
            Assert.IsFalse(Date.IsLeapYear(1722), "Year 1722");
            Assert.IsFalse(Date.IsLeapYear(1721), "Year 1721");
            Assert.IsFalse(Date.IsLeapYear(1720), "Year 1720");
            Assert.IsTrue(Date.IsLeapYear(1719), "year 1719");
        }

        [TestMethod]
        public void TestGetMonthName()
        {
            Assert.AreEqual("Thout", Date.GetMonthName(1), "Month 1");
            Assert.AreEqual("Paopi", Date.GetMonthName(2), "Month 2");
            Assert.AreEqual("Hathor", Date.GetMonthName(3), "Month 3");
            Assert.AreEqual("Koiak", Date.GetMonthName(4), "Month 4");
            Assert.AreEqual("Tobi", Date.GetMonthName(5), "Month 5");
            Assert.AreEqual("Meshir", Date.GetMonthName(6), "Month 6");
            Assert.AreEqual("Paremhat", Date.GetMonthName(7), "Month 7");
            Assert.AreEqual("Paremoude", Date.GetMonthName(8), "Month 8");
            Assert.AreEqual("Pashons", Date.GetMonthName(9), "Month 9");
            Assert.AreEqual("Paoni", Date.GetMonthName(10), "Month 10");
            Assert.AreEqual("Epip", Date.GetMonthName(11), "Month 11");
            Assert.AreEqual("Mesori", Date.GetMonthName(12), "Month 12");
            Assert.AreEqual("Pi Kogi Enavot", Date.GetMonthName(13), "Month 13");
        }

        [TestMethod]
        public void TestGetEasterDate()
        {
            /*
            May 1	2005 - 
            Apr 23	2006
            Apr 8	2007
            Apr 27	2008
            Apr 19	2009
            Apr 4	2010
            Apr 24	2011
            Apr 15	2012
            May 5	2013
            Apr 20	2014 - 12/8/1730
            Apr 12	2015 - 4/8/1731
            May 1	2016 - 23/8/1732
            Apr 16	2017 - 8/8/1733
            Apr 8	2018
            Apr 28	2019
            Apr 19	2020
            */

            Assert.AreEqual(new Date(1730, 8, 12), Date.GetEasterDate(1730), "Easter 1730");
            Assert.AreEqual(new Date(1731, 8, 4), Date.GetEasterDate(1731), "Easter 1731");
            Assert.AreEqual(new Date(1732, 8, 23), Date.GetEasterDate(1732), "Easter 1732");
            Assert.AreEqual(new Date(1733, 8, 8), Date.GetEasterDate(1733), "Easter 1733");

        }
        #endregion

        #region Properties
        [TestMethod]
        public void TestDayInYear()
        {
            Assert.AreEqual(16, new Date(1730, 1, 16).DayInYear, "16/1/1730");
            Assert.AreEqual(46, new Date(1730, 2, 16).DayInYear, "16/2/1730");
            Assert.AreEqual(30, new Date(1730, 1, 30).DayInYear, "30/1/1730");
            Assert.AreEqual(1, new Date(1730, 1, 1).DayInYear, "1/1/1730");
            Assert.AreEqual(362, new Date(1730, 13, 2).DayInYear, "2/13/1730");
        }

        [TestMethod]
        public void TestToGregorianDate()
        {
            Assert.AreEqual(new DateTime(284, 8, 29), new Date(1, 1, 1).ToGregorianDate, "29/8/284");
            Assert.AreEqual(new DateTime(2014, 1, 7), new Date(1730, 4, 29).ToGregorianDate, "7/1/2014");
            Assert.AreEqual(new DateTime(2014, 4, 20), new Date(1730, 8, 12).ToGregorianDate, "20/4/2014");
        }

        [TestMethod]
        public void TestAbsoluteDays()
        {
            Assert.AreEqual(1, new Date(1, 1, 1).AbsoluteDays, "1/1/1");
            Assert.AreEqual(366, new Date(2, 1, 1).AbsoluteDays, "1/1/2");
            Assert.AreEqual(731, new Date(3, 1, 1).AbsoluteDays, "1/1/3");
            Assert.AreEqual(1097, new Date(4, 1, 1).AbsoluteDays, "1/1/4");
            Assert.AreEqual(1462, new Date(5, 1, 1).AbsoluteDays, "1/1/5");
        }
        #endregion

        #region Methods
        [TestMethod]
        public void TestSubtractDays()
        {
            // Subtracting days within the month
            Assert.AreEqual(new Date(1730, 8, 8), new Date(1730, 8, 12).SubtractDays(4), "12/8/1730 - 4");
            Assert.AreEqual(new Date(1730, 8, 2), new Date(1730, 8, 12).SubtractDays(10), "12/8/1730 - 10");
            Assert.AreEqual(new Date(1730, 8, 1), new Date(1730, 8, 12).SubtractDays(11), "12/8/1730 - 11");
        }

        [TestMethod]
        public void TestAddDays()
        {
            // Adding days within the month
            Assert.AreEqual(new Date(1730, 8, 16), new Date(1730, 8, 12).AddDays(4), "12/8/1730 + 4");
            Assert.AreEqual(new Date(1730, 8, 29), new Date(1730, 8, 12).AddDays(17), "12/8/1730 + 17");
            Assert.AreEqual(new Date(1730, 8, 30), new Date(1730, 8, 12).AddDays(18), "12/8/1730 + 18");
            // Adding days towards next month
            Assert.AreEqual(new Date(1730, 9, 1), new Date(1730, 8, 12).AddDays(19), "12/8/1730 + 19");
            Assert.AreEqual(new Date(1730, 9, 2), new Date(1730, 8, 12).AddDays(20), "12/8/1730 + 20");
            // Adding days at the edge towards next month
            Assert.AreEqual(new Date(1730, 12, 30), new Date(1730, 12, 29).AddDays(1), "29/12/1730 + 1");
            Assert.AreEqual(new Date(1730, 13, 1), new Date(1730, 12, 29).AddDays(2), "29/12/1730 + 2");
            // Adding days at the edge towards next year not leap year
            Assert.AreEqual(new Date(1730, 13, 4), new Date(1730, 12, 29).AddDays(5), "29/12/1730 + 5");
            Assert.AreEqual(new Date(1730, 13, 5), new Date(1730, 12, 29).AddDays(6), "29/12/1730 + 6");
            Assert.AreEqual(new Date(1731, 1, 1), new Date(1730, 12, 29).AddDays(7), "29/12/1730 + 7");
            Assert.AreEqual(new Date(1731, 1, 2), new Date(1730, 12, 29).AddDays(8), "29/12/1730 + 8");
            // Adding days at the edge towards next year leap year
            Assert.AreEqual(new Date(1727, 13, 4), new Date(1727, 12, 29).AddDays(5), "27/12/1730 + 5");
            Assert.AreEqual(new Date(1727, 13, 5), new Date(1727, 12, 29).AddDays(6), "27/12/1730 + 6");
            Assert.AreEqual(new Date(1727, 13, 6), new Date(1727, 12, 29).AddDays(7), "27/12/1730 + 7");
            Assert.AreEqual(new Date(1728, 1, 1), new Date(1727, 12, 29).AddDays(8), "27/12/1730 + 8");
            // Adding a whole month
            Assert.AreEqual(new Date(1730, 9, 22), new Date(1730, 8, 12).AddDays(40), "12/8/1730 + 40");
            // Adding multiple months
            Assert.AreEqual(new Date(1730, 11, 22), new Date(1730, 8, 12).AddDays(100), "12/8/1730 + 100");
            Assert.AreEqual(new Date(1731, 2, 27), new Date(1730, 8, 12).AddDays(200), "12/8/1730 + 200");
            // Adding a whole year not leap
            Assert.AreEqual(new Date(1731, 8, 12), new Date(1730, 8, 12).AddDays(365), "12/8/1730 + 365");
            // Adding a whole year a leap year
            Assert.AreEqual(new Date(1728, 8, 11), new Date(1727, 8, 12).AddDays(365), "12/8/1727 + 365");
            Assert.AreEqual(new Date(1728, 8, 12), new Date(1727, 8, 12).AddDays(366), "12/8/1727 + 366");
            // Adding three years no leap years
            Assert.AreEqual(new Date(1731, 4, 15), new Date(1728, 4, 10).AddDays(1100), "10/4/1728 + 1100");
            // Adding four years including a leap year
            Assert.AreEqual(new Date(1732, 5, 19), new Date(1728, 4, 10).AddDays(1500), "10/4/1728 + 1500");
        }

        [TestMethod]
        public void TestEquals()
        {
            Assert.IsTrue(new Date(10, 1, 1).Equals(new Date(10, 1, 1)));
            Assert.IsTrue(new Date(100, 1, 10).Equals(new Date(100, 1, 10)));
            Assert.IsFalse(new Date(10, 1, 1).Equals(new Date(10, 2, 1)));
            Assert.IsFalse(new Date(10, 1, 1).Equals(new Date(10, 1, 3)));
            Assert.IsFalse(new Date(10, 1, 1).Equals(new Date(20, 1, 1)));
        }

        [TestMethod]
        public void TestToString()
        {
            Assert.AreEqual("1 Thout 10", new Date(10, 1, 1).ToString());
        }
        #endregion
    }
}
